diff --git a/async/src/main/java/asyncservice/ExampleService.java b/async/src/main/java/asyncservice/ExampleService.java new file mode 100644 index 0000000..1c07c0c --- /dev/null +++ b/async/src/main/java/asyncservice/ExampleService.java @@ -0,0 +1,44 @@ +package asyncservice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +class ExampleService { + + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); + + String work() { + sleep(7000, TimeUnit.MILLISECONDS); /* Pretend to be busy... */ + char[] str = new char[5]; + ThreadLocalRandom current = ThreadLocalRandom.current(); + for (int idx = 0; idx < str.length; ++idx) + str[idx] = (char) ('A' + current.nextInt(26)); + String msg = new String(str); + log("Generated message: " + msg); + return msg; + } + + private void sleep(long average, TimeUnit unit) { + String name = Thread.currentThread().getName(); + long timeout = Math.min(exponential(average), Math.multiplyExact(10, average)); + log("{} sleeping {} {}...", name, timeout, unit); + try { + unit.sleep(timeout); + log(name + " awoke."); + } catch (InterruptedException abort) { + Thread.currentThread().interrupt(); + log(name + " interrupted."); + } + } + + private long exponential(long avg) { + return (long) (avg * -Math.log(1 - ThreadLocalRandom.current().nextDouble())); + } + + private void log(String s, Object... o) { + LOGGER.info(s, o); + } +} diff --git a/async/src/main/java/asyncservice/Main.java b/async/src/main/java/asyncservice/Main.java new file mode 100644 index 0000000..e8f9c03 --- /dev/null +++ b/async/src/main/java/asyncservice/Main.java @@ -0,0 +1,49 @@ +package asyncservice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.*; + + +public class Main { + + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); + + private static Callable lazyval(T val, long timeout) { + return () -> { + Thread.sleep(1000); + log("Task completed with : " + val); + return val; + }; + } + + public static void main(String[] args) throws InterruptedException { + ExecutorService executor = Executors.newFixedThreadPool(4); + final Future asyncRes = executor.submit(lazyval(1, 100L)); + final Future asyncRes1 = executor.submit(lazyval("Hello", 20L)); + final Future asyncRes2 = executor.submit(lazyval("World", 100L)); + Thread.sleep(15000); + log("End of executor example."); + executor.shutdown(); + getTaskNotificationWithoutBlocking(); + } + + private static void getTaskNotificationWithoutBlocking() throws InterruptedException { + ExampleService svc = new ExampleService(); + CompletableFuture future = CompletableFuture.supplyAsync(svc::work); + Main listener = new Main(); + future.thenAccept(listener::notify); + Thread.sleep(10000); + log("Exciting main..."); + } + + private void notify(String s) { + log("Received message ", s); + } + + private static void log(String s, Object... o) { + LOGGER.info(s, o); + } + +} diff --git a/async/src/main/java/threadexamples/BasicThreadExample.java b/async/src/main/java/threadexamples/BasicThreadExample.java new file mode 100644 index 0000000..51453f7 --- /dev/null +++ b/async/src/main/java/threadexamples/BasicThreadExample.java @@ -0,0 +1,24 @@ +package threadexamples; + +public class BasicThreadExample extends Thread { + + private static int cpt = 0; + + @Override + public void run() { + int tmp = cpt; + for (int i = 0; i < 1000000; i++); + tmp++; + cpt = tmp; + } + + public static void main(String[] args) throws InterruptedException { + Thread thread1 = new BasicThreadExample(); + Thread thread2 = new BasicThreadExample(); + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + System.out.println("Compteur : " + cpt); + } +} diff --git a/async/src/main/java/threadexamples/producerconsumer/App.java b/async/src/main/java/threadexamples/producerconsumer/App.java new file mode 100644 index 0000000..fcfc904 --- /dev/null +++ b/async/src/main/java/threadexamples/producerconsumer/App.java @@ -0,0 +1,40 @@ +package threadexamples.producerconsumer; + +import org.slf4j.LoggerFactory; + +import java.util.concurrent.*; + +public class App { + public static void main(String[] args) { + BlockingQueue itemQueue = new LinkedBlockingQueue<>(5); + ExecutorService executorService = Executors.newFixedThreadPool(5); + + for (int i = 0; i < 2; i++) { + final Producer producer = new Producer("Producer_" + i, itemQueue); + executorService.submit(() -> { + while (true) { + producer.produce(); + } + }); + } + + for (int i = 0; i < 3; i++) { + final Consumer consumer = new Consumer("Consumer_" + i, itemQueue); + executorService.submit(() -> { + while(true) { + consumer.consume(); + } + }); + } + + executorService.shutdown(); + + try { + executorService.awaitTermination(10, TimeUnit.SECONDS); + executorService.shutdownNow(); + } catch (InterruptedException e) { + LoggerFactory.getLogger(App.class).error("Error waiting for ExecutorService shutdown"); + } + + } +} diff --git a/async/src/main/java/threadexamples/producerconsumer/Consumer.java b/async/src/main/java/threadexamples/producerconsumer/Consumer.java new file mode 100644 index 0000000..49b32a7 --- /dev/null +++ b/async/src/main/java/threadexamples/producerconsumer/Consumer.java @@ -0,0 +1,25 @@ +package threadexamples.producerconsumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.BlockingQueue; + +public class Consumer { + + private static final Logger LOGGER = LoggerFactory.getLogger(Consumer.class); + + private BlockingQueue itemQueue; + private String name; + private int itemId; + + public Consumer(String name, BlockingQueue itemQueue) { + this.itemQueue = itemQueue; + this.name = name; + } + + public void consume() throws InterruptedException { + final Item item = itemQueue.take(); + LOGGER.info("Consumer [{}] consume item [{}] produced by [{}]", name, item.getId(), item.getProducer()); + } +} diff --git a/async/src/main/java/threadexamples/producerconsumer/Item.java b/async/src/main/java/threadexamples/producerconsumer/Item.java new file mode 100644 index 0000000..9b300b3 --- /dev/null +++ b/async/src/main/java/threadexamples/producerconsumer/Item.java @@ -0,0 +1,21 @@ +package threadexamples.producerconsumer; + +public class Item { + + private String producer; + private int id; + + public Item(String producer, int id) { + this.producer = producer; + this.id = id; + } + + public String getProducer() { + return producer; + } + + public int getId() { + return id; + } + +} diff --git a/async/src/main/java/threadexamples/producerconsumer/Producer.java b/async/src/main/java/threadexamples/producerconsumer/Producer.java new file mode 100644 index 0000000..aad2efd --- /dev/null +++ b/async/src/main/java/threadexamples/producerconsumer/Producer.java @@ -0,0 +1,21 @@ +package threadexamples.producerconsumer; + +import java.util.Random; +import java.util.concurrent.BlockingQueue; + +public class Producer { + + private BlockingQueue itemQueue; + private String name; + private int itemId; + + public Producer(String name, BlockingQueue itemQueue) { + this.itemQueue = itemQueue; + this.name = name; + } + + public void produce() throws InterruptedException { + itemQueue.put(new Item(name, itemId++)); + Thread.sleep(new Random().nextInt(2000)); + } +} diff --git a/gui/pom.xml b/gui/pom.xml new file mode 100644 index 0000000..4a78421 --- /dev/null +++ b/gui/pom.xml @@ -0,0 +1,15 @@ + + + + java-cookbook + fr.gasser + 1.0-SNAPSHOT + + 4.0.0 + + gui + + + \ No newline at end of file diff --git a/gui/src/main/java/fr/gasser/gui/Mandelbrot.java b/gui/src/main/java/fr/gasser/gui/Mandelbrot.java new file mode 100644 index 0000000..ec7ff63 --- /dev/null +++ b/gui/src/main/java/fr/gasser/gui/Mandelbrot.java @@ -0,0 +1,84 @@ +package fr.gasser.gui; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +public class Mandelbrot extends JFrame { + + private static final int imgSize = 256; + + private final BufferedImage img; + + private Mandelbrot(double xc, double yc, double size) throws HeadlessException { + super("fr.gasser.gui.Mandelbrot set"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + int max = 255; // maximum number of iterations + + setPreferredSize(new Dimension(imgSize, imgSize)); + setMinimumSize(getPreferredSize()); + setMaximumSize(getPreferredSize()); + setResizable(false); + + + img = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB); + for (int i = 0; i < imgSize; i++) { + for (int j = 0; j < imgSize; j++) { + double x0 = xc - size/2 + size*i/imgSize; + double y0 = yc - size/2 + size*j/imgSize; + Complex z0 = new Complex(x0, y0); + int gray = max - mand(z0, max); + Color color = new Color(gray, gray, gray); + img.setRGB(i, imgSize-1-j, color.getRGB()); + } + } + + setVisible(true); + } + + // return number of iterations to check if c = a + ib is in fr.gasser.gui.Mandelbrot set + private int mand(Complex z0, int max) { + Complex z = z0; + for (int t = 0; t < max; t++) { + if (z.abs() > 2.0) return t; + z = z.times(z).plus(z0); + } + return max; + } + + @Override + public void paint(Graphics g) { + g.drawImage(img, 0, 0, this); + } + + private static class Complex { + + private final double re; + private final double im; + + Complex(double real, double imaginary) { + this.re = real; + this.im = imaginary; + } + + double abs() { + return re * re + im * im; + } + + Complex times(Complex b) { + Complex a = this; + double real = a.re * b.re - a.im * b.im; + double imag = a.re * b.im + a.im * b.re; + return new Complex(real, imag); + } + + Complex plus(Complex other) { + return new Complex(this.re + other.re, this.im + other.im); + } + } + + public static void main(String[] args) { + + EventQueue.invokeLater(() -> new Mandelbrot(-1, -0.25, 0.25)); + } +} \ No newline at end of file diff --git a/gui/src/main/java/fr/gasser/gui/editor/App.java b/gui/src/main/java/fr/gasser/gui/editor/App.java new file mode 100644 index 0000000..ec3f9dc --- /dev/null +++ b/gui/src/main/java/fr/gasser/gui/editor/App.java @@ -0,0 +1,141 @@ +package fr.gasser.gui.editor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.*; +import javax.swing.text.*; +import java.awt.*; +import java.awt.event.ItemEvent; + +public class App extends JFrame { + + private final static Logger LOGGER = LoggerFactory.getLogger(App.class); + private static final Font DEFAULT_FONT = new Font("Arial", Font.PLAIN, 14); + private static final Color DEFAULT_COLOR = Color.BLACK; + private static final String LOREM_IPSUM = "Quisque eget mattis nulla. Praesent efficitur fermentum neque quis fringilla. Quisque laoreet eros maximus nibh eleifend, eu faucibus tortor egestas. Proin finibus eu tortor eget dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam quis nisi sit amet magna ullamcorper sollicitudin. Curabitur vel pellentesque lorem, in tristique dolor."; + + private JTextPane editArea; + private JButton colorButton; + + private App() throws HeadlessException { + setDefaultCloseOperation(EXIT_ON_CLOSE); + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + LOGGER.warn(e.getMessage()); + } + initComponents(); + pack(); + setVisible(true); + } + + private void initComponents() { + setLayout(new BorderLayout()); + final JPanel topPane = new JPanel(); + topPane.setLayout(new BorderLayout()); + final JPanel bottomPane = new JPanel(); + + editArea = new JTextPane(); + editArea.setText(LOREM_IPSUM); + editArea.setFont(DEFAULT_FONT); + final JScrollPane textScrollPane = new JScrollPane(editArea); + textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + textScrollPane.setPreferredSize(new Dimension(144, 256)); + topPane.add(textScrollPane); + + final FontChooser fontChooser = new FontChooser(); + fontChooser.addItemListener(this::doChangeFont); + fontChooser.setSelectedItem(fontChooser.get(DEFAULT_FONT)); + bottomPane.add(fontChooser); + + final JComboBox fontSizeChooser = new JComboBox<>(); + for (int i=5; i < 200; i++) fontSizeChooser.addItem(i); + fontSizeChooser.addItemListener(this::doChangeFontSize); + fontSizeChooser.setSelectedItem(DEFAULT_FONT.getSize()); + bottomPane.add(fontSizeChooser); + + colorButton = new JButton(); + colorButton.setHorizontalAlignment(SwingConstants.CENTER); + colorButton.setVerticalAlignment(SwingConstants.CENTER); + colorButton.setIcon(new SquareIcon(30, DEFAULT_COLOR)); + colorButton.addActionListener(evt -> doChangeColor()); + bottomPane.add(colorButton); + + final JButton boldButton = new JButton(new StyledEditorKit.BoldAction()); + boldButton.setText("B"); + bottomPane.add(boldButton); + + final JButton italicButton = new JButton(new StyledEditorKit.ItalicAction()); + italicButton.setText("I"); + bottomPane.add(italicButton); + + final JButton underlineButton = new JButton(new StyledEditorKit.UnderlineAction()); + underlineButton.setText("U"); + bottomPane.add(underlineButton); + + final Container container = getContentPane(); + container.add(topPane, BorderLayout.NORTH); + container.add(bottomPane, BorderLayout.SOUTH); + } + + private void doChangeColor() { + final Color c = JColorChooser.showDialog(this, "Choose a color", Color.BLACK); + SimpleAttributeSet attr = new SimpleAttributeSet(getSelectedElement().getAttributes().copyAttributes()); + StyleConstants.setForeground(attr, c); + editArea.setCharacterAttributes(attr, true); + colorButton.setIcon(new SquareIcon(30, c)); + + } + + private void doChangeFont(ItemEvent evt) { + final Font f = (Font) evt.getItem(); + + SimpleAttributeSet attr = new SimpleAttributeSet(getSelectedElement().getAttributes().copyAttributes()); + StyleConstants.setFontFamily(attr, f.getName()); + editArea.setCharacterAttributes(attr, true); + } + + private void doChangeFontSize(ItemEvent evt) { + SimpleAttributeSet attr = new SimpleAttributeSet(getSelectedElement().getAttributes().copyAttributes()); + StyleConstants.setFontSize(attr, (int) evt.getItem()); + editArea.setCharacterAttributes(attr, true); + } + + private Element getSelectedElement() { + final StyledDocument doc = (StyledDocument) editArea.getDocument(); + return doc.getCharacterElement(editArea.getSelectionStart()); + } + + public static void main(String[] args) { + EventQueue.invokeLater(App::new); + } + + private static class SquareIcon implements Icon { + private final int size; + private final Color color; + + SquareIcon(int size, Color color) { + this.size = size; + this.color = color; + } + + @Override + public void paintIcon(Component component, Graphics graphics, int i, int i1) { + final Point center = new Point(component.getWidth()/4, component.getHeight()/4); + final Graphics2D g2d = (Graphics2D) graphics; + g2d.setColor(color); + g2d.fillRect(center.x, center.y, size, size); + } + + @Override + public int getIconWidth() { + return size; + } + + @Override + public int getIconHeight() { + return size; + } + } +} diff --git a/gui/src/main/java/fr/gasser/gui/editor/FontChooser.java b/gui/src/main/java/fr/gasser/gui/editor/FontChooser.java new file mode 100644 index 0000000..4cb688d --- /dev/null +++ b/gui/src/main/java/fr/gasser/gui/editor/FontChooser.java @@ -0,0 +1,56 @@ +package fr.gasser.gui.editor; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.Comparator; + +class FontChooser extends JComboBox { + + FontChooser() { + + final Font[] fonts = GraphicsEnvironment + .getLocalGraphicsEnvironment() + .getAllFonts(); + + Arrays.sort(fonts, Comparator.comparing(Font::getName)); + + for (Font font : fonts) { + if (font.canDisplayUpTo(font.getName()) == -1) { + addItem(font); + } + } + + setRenderer(new FontCellRenderer()); + } + + Font get(Font font) { + for (int i = 0; i < getModel().getSize(); i++) { + final Font f = getItemAt(i); + if (f.getName().equals(font.getName())) return f; + } + return null; + } + + private static class FontCellRenderer + implements ListCellRenderer { + + DefaultListCellRenderer renderer = new DefaultListCellRenderer(); + + public Component getListCellRendererComponent( + JList list, Font font, int index, + boolean isSelected, boolean cellHasFocus) { + + final Component result = renderer.getListCellRendererComponent( + list, font.getName(), index, isSelected, cellHasFocus); + + setFontPreserveSize(result, font); + return result; + } + } + + private static void setFontPreserveSize(final Component comp, Font font) { + final float size = comp.getFont().getSize(); + comp.setFont(font.deriveFont(size)); + } +} \ No newline at end of file diff --git a/gui/src/main/java/fr/gasser/gui/test/Main.java b/gui/src/main/java/fr/gasser/gui/test/Main.java new file mode 100644 index 0000000..7802eb0 --- /dev/null +++ b/gui/src/main/java/fr/gasser/gui/test/Main.java @@ -0,0 +1,61 @@ +package fr.gasser.gui.test; + +import javax.swing.*; +import java.awt.*; + +public class Main extends JFrame { + + private static final String TEXT = "你好,世界"; + private final Model model; + private final View view; + + private Main() { + super("Demo"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + + // Initialize model + model = new Model( + new Font("Fira Sans", Font.PLAIN, 50), + new Point(50, 50), + Color.RED, TEXT + ); + + view = new View(model); + getContentPane().add(view); + + setJMenuBar(createMenuBar()); + + pack(); + setVisible(true); + } + + private JMenuBar createMenuBar() { + final JMenuBar menuBar = new JMenuBar(); + final JButton bold = new JButton("B"); + bold.addActionListener(evt -> { + model.setBold(!model.getFont().isBold()); + view.repaint(); + }); + menuBar.add(bold); + + final JButton italic = new JButton("I"); + italic.addActionListener(evt -> { + model.setItalic(!model.getFont().isItalic()); + view.repaint(); + }); + menuBar.add(italic); + + final JButton antiAlias = new JButton("AA"); + antiAlias.addActionListener(evt -> { + model.setAntialias(!model.isAntialiased()); + view.repaint(); + }); + menuBar.add(antiAlias); + + return menuBar; + } + + public static void main(String[] args) { + EventQueue.invokeLater(Main::new); + } +} diff --git a/gui/src/main/java/fr/gasser/gui/test/Model.java b/gui/src/main/java/fr/gasser/gui/test/Model.java new file mode 100644 index 0000000..facd8b7 --- /dev/null +++ b/gui/src/main/java/fr/gasser/gui/test/Model.java @@ -0,0 +1,51 @@ +package fr.gasser.gui.test; + +import java.awt.*; + +class Model { + + private Font font; + private Point pos; + private Paint paint; + private String text; + private boolean antialiased = true; + + Model(Font font, Point pos, Paint paint, String text) { + this.font = font; + this.pos = pos; + this.paint = paint; + this.text = text; + } + + Font getFont() { + return font; + } + + Point getPos() { + return pos; + } + + Paint getPaint() { + return paint; + } + + String getText() { + return text; + } + + boolean isAntialiased() { + return antialiased; + } + + void setAntialias(boolean b) { + this.antialiased = b; + } + + void setBold(boolean bold) { + this.font = font.deriveFont(bold ? Font.BOLD : Font.PLAIN); + } + + void setItalic(boolean italic) { + this.font = font.deriveFont(italic ? Font.ITALIC : Font.PLAIN); + } +} diff --git a/gui/src/main/java/fr/gasser/gui/test/View.java b/gui/src/main/java/fr/gasser/gui/test/View.java new file mode 100644 index 0000000..78afdf7 --- /dev/null +++ b/gui/src/main/java/fr/gasser/gui/test/View.java @@ -0,0 +1,43 @@ +package fr.gasser.gui.test; + +import javax.swing.*; +import java.awt.*; +import java.awt.font.TextAttribute; +import java.util.Hashtable; +import java.util.Map; + +public class View extends JPanel { + + private Model model; + View(Model model) { + this.model = model; + setPreferredSize(getPreferredSize()); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(640, 360); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, + model.isAntialiased() ? + RenderingHints.VALUE_TEXT_ANTIALIAS_ON : + RenderingHints.VALUE_TEXT_ANTIALIAS_OFF + ); + + g2d.setFont(model.getFont()); + g2d.setPaint(model.getPaint()); + + g2d.drawString(model.getText(), model.getPos().x, model.getPos().y); + } + + public void setModel(Model model) { + this.model = model; + } +} diff --git a/pom.xml b/pom.xml index 8c93729..3f97cb9 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,7 @@ reflection + gui @@ -15,5 +16,19 @@ 1.8 1.8 + + + + org.slf4j + slf4j-api + RELEASE + + + + org.slf4j + slf4j-simple + RELEASE + + \ No newline at end of file