Latest

Оптимизация Merge Index в MySQL

Две отличные статьи касательно оптимизации запросов через OR в MySQL: раз, два.

Deadlock в приложениях на Java

Во время работы игрового сервера, несколько раз было замечено возникновение deadlock’ов. Обнаружить причину их возникновения пока непосильная задача, поэтому дабы избежать вытекающих отсюда проблем – делается рестарт сервака.

Решение простое, так как в стандартной поставке JDK есть нужный нам класс: ThreadMXBean, который можно получить путем вызова: ManagementFactory.getThreadMXBean().

Реализация:

import java.lang.management.*;
import java.util.logging.Logger;

public class DeadLockDetector implements Runnable {

    private static final Logger log = Logger.getLogger(DeadLockDetector.class.getName());

    private static DeadLockDetector instance;

    private final ThreadMXBean tmx;

    public static DeadLockDetector getInstance() {
        return instance;
    }

    private DeadLockDetector() {
        tmx = ManagementFactory.getThreadMXBean();
        instance = this;
    }

    public final void run() {

        boolean deadlock = false;
        while (!deadlock) {
            try {
                long[] ids = tmx.findDeadlockedThreads();

                // Deadlock detected
                if (ids != null) {

                    deadlock = true;
                    ThreadInfo[] tis = tmx.getThreadInfo(ids, true, true);
                    String info = "DeadLock Found!\n";
                    for (ThreadInfo ti : tis) {
                        info += ti.toString();
                    }

                    for (ThreadInfo ti : tis) {
                        LockInfo[] locks = ti.getLockedSynchronizers();
                        MonitorInfo[] monitors = ti.getLockedMonitors();
                        if (locks.length == 0 && monitors.length == 0) {
                            // This thread isn't a reason, it's just blocked by external deadlock
                            continue;
                        }

                        ThreadInfo dl = ti;
                        info += "Java-level deadlock:\n";
                        info += "\t" + dl.getThreadName() + " is waiting to lock " + dl.getLockInfo().toString() + " which is held by " + dl.getLockOwnerName() + "\n";
                        while ((dl = tmx.getThreadInfo(new long[]{dl.getLockOwnerId()}, true, true)[0]).getThreadId() != ti.getThreadId()) {
                            info += "\t" + dl.getThreadName() + " is waiting to lock " + dl.getLockInfo().toString() + " which is held by " + dl.getLockOwnerName() + "\n";
                        }
                    }

                    //                    listenerEngine.methodInvoked(DeadlockListener.class);

                    log.severe(info);
                    log.severe("Shutting down server with exit code = 2, startup script will do authomatic restart.");
                    System.exit(2);
                }

                Thread.sleep(200);
            }
            catch (Exception e) {
                log.severe(e.getLocalizedMessage());
            }
        }
    }

    public static void start() {
        Thread t = new Thread(new DeadLockDetector());
        t.setName("DeadLock Monitor");
        t.setDaemon(true);
        t.setPriority(Thread.MAX_PRIORITY);
        t.start();
        log.info("DeadLock Detector started.");
    }
}

Сжатие *.mp3 файлов с помощью Lame

Скачиваем последнюю версию Lame. Далее для автоматизации процесса используем Java (увы скриптовые языки я не знаю).

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;

/**
 * User: Babanin
 * Date: 04.09.2010
 * Time: 14:36:04
 */
public class LameQuality {
    private static final String DIR_TO_CONVERT = "C:\\fileToConvert";
    private static final String LAME_FILE_DIR = "C:\\lame\\";
    private static final String LAME_FILENAME = "lame.exe";
    private static final String LAME_OPTIONS = "-V9";
    private static final String PREFIX = "converted_";

    public static void main(String[] args) {
        File baseDir = new File(DIR_TO_CONVERT);

        if (baseDir.isDirectory()) {
            File[] files = baseDir.listFiles(new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.endsWith(".mp3");
                }
            });

            for (File inputFile : files) {
                try {
                    String inputFilePath = inputFile.getAbsolutePath();
                    File outputFile = new File(inputFilePath.substring(0, inputFilePath.lastIndexOf("\\") + 1) + PREFIX +
                            inputFilePath.substring(inputFilePath.lastIndexOf("\\") + 1));

                    if (outputFile.exists()) {
                        //noinspection ResultOfMethodCallIgnored
                        outputFile.delete();
                    }

                    ProcessBuilder builder = new ProcessBuilder(LAME_FILE_DIR + LAME_FILENAME, LAME_OPTIONS,
                            inputFile.getAbsolutePath(), outputFile.getAbsolutePath());
                    builder.directory(new File(LAME_FILE_DIR)).start();

                    System.out.println("Process: " + LAME_FILE_DIR + LAME_FILENAME + " " + LAME_OPTIONS + " " +
                            inputFile.getAbsolutePath() + " " + outputFile.getAbsolutePath());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            System.err.println(DIR_TO_CONVERT + " is not a directory");
        }
    }
}

Указываем через константы путь к Lame (LAME_FILE_DIR), путь к *.mp3 файлам (DIR_TO_CONVERT), настройки Lame (LAME_OPTIONS), а также если нужно указываем префикс (PREFIX). Затем запускаем данный класс.

Увы, но Process.waitFor() упорно блокировал процесс и я так не разобрался почему.

Развертывание WAR в корне JBoss

Чтобы развернуть веб приложене (*.war) в корне JBoss (то есть ‘/’), необходимо:

  1. удалить ROOT.war в папке deploy (к примеру, у JBoss 6.0 это /server/default/deploy/)
  2. добавить jboss-web.xml в папку WEB-INF со следующим содержимым:
    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
        <context-root />
    </jboss-web>
    

Рестарт сервера и наше приложение доступно по адрес http://localhost:8080/.

Hibernate & PostgreSQL problems

Я только что начал новый проект и выбрал Intellij Idea 9.0.3 как среду для разработки, БД: PostgreSQL 8.4, сервер приложений: JBoss 6.0 m4.

Создадим простейший класс Customer.java:

@Entity()
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column()
    private String name;

    @Column()
    private String patronymic;

    @Column()
    private String surname;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPatronymic() {
        return patronymic;
    }

    public void setPatronymic(String patronymic) {
        this.patronymic = patronymic;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }
}

and by default idea create a persistence.xml file for me:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">

    <persistence-unit name="RavenPersistenceUnit">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <jta-data-source>java:/RavenDS</jta-data-source>         
        <class>com.silverhaze.raven.common.data.Customer</class>
        <properties>
            <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/raven"/>
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
            <property name="hibernate.connection.username" value="postgres"/>
            <property name="hibernate.connection.password" value="password"/>
            <!--<property name="hibernate.archive.autodetection" value="class"/>-->
            <!--<property name="hibernate.show_sql" value="true"/>-->
            <!--<property name="hibernate.format_sql" value="true"/>-->
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
            <property name="hbm2ddl.auto" value="update"/>
        </properties>

    </persistence-unit>
</persistence>

После развертывания приложения, я получаю следующую ошибку:

ERROR JDBCExceptionReporter:78 – ERROR: relation “hibernate_sequence” does not exist
Exception in thread “main” org.hibernate.exception.SQLGrammarException: could not get next sequence value

Решение простое, нужно просто добавить sequence в текущую БД:

CREATE SEQUENCE hibernate_sequence
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 33
  CACHE 1;
ALTER TABLE hibernate_sequence OWNER TO postgres;

После того как я попытался сохранить сущность Customer в БД, я обнаружил, что в БД отсутствует таблица customer, но Hibernate должен был создать её, так как я использовал следующие настройки:

<property name="hbm2ddl.auto" value="update"/>

Я просмотрел persistence.xml несколько раз и вроде бы всё корректно. После этого я решил перечитать документацию касательно hibernate properties и обнаружил ошибку. Проблема заключалась в том, что в persistence.xml, который по умолчанию сгенерировала IntelliJ IDEA был указан параметр hbm2ddl.auto вместо hibernate.hbm2ddl.auto.